package app

import (
	"fmt"
	"io/ioutil"
	"log"
	"lowcode_deployment/pkg/config"
	"os"
)

type Command struct {
	Id                string `json:"id"`
	Type              string `json:"type"`
	AppId             int32  `json:"appId"`
	TenantId          int32  `json:"tenantId"`
	Version           string `json:"version"`
	Env               string `json:"env"`
	Port              int32  `json:"port"`
	CodeSrcPath       string `json:"codeSrcPath"`
	CodeRunPath       string `json:"codeRunPath"`
	CodeBakPath       string `json:"codeBakPath"`
	LogPath           string `json:"logPath"`
	LowcodeCode       string `json:"lowcodeCode"`
	WorkspaceRoot     string `json:"workspaceRoot"`
	AppConfigFilePath string `json:"appConfigFilePath"`
	AppUploadFilePath string `json:"appUploadFilePath"`
}

func (cmd *Command) Exec() error {
	var err error

	// 删除历史日志文件
	logPath := cmd.LogPath
	folderPath := getParentDirectory(logPath)
	exists, _ := isFileExist(folderPath)
	if !exists {
		os.Mkdir(folderPath, os.ModePerm)
	}
	exists, _ = isFileExist(logPath)
	if exists {
		os.Remove(logPath)
	}

	switch cmd.Type {
	case "init":
		err = cmd.initApp()
		break
	case "preview":
		err = cmd.previewApp()
		break
	case "publish":
		err = cmd.publishApp()
		break
	case "rollback":
		err = cmd.rollbackApp()
		break
	}
	if err != nil {
		return err
	}

	return nil
}

func (cmd *Command) initApp() error {
	// 1. 如果是开发环境，需初始化应用模板
	if "develop" == cmd.Env {
		err := cmd.initDevCode()
		if err != nil {
			return err
		}
	}

	// 2. 初始化nginx配置
	err := cmd.initNginxConf()
	if err != nil {
		return err
	}

	// 3. 初始化代码
	err = cmd.initCodeDir()
	if err != nil {
		return err
	}

	// 4. 重载nginx配置
	err = cmd.reloadNginx()
	if err != nil {
		return err
	}
	return nil

}

func (cmd *Command) initNginxConf() error {
	conf := fmt.Sprintf("app_%d_%s.conf", cmd.AppId, cmd.Env)
	confPath := config.ENV["NGINX_CONF_DIR"] + conf
	log.Println("create nginx conf ", confPath)
	fd, err := os.OpenFile(confPath, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return err
	}

	confContent := cmd.getNginxConf()
	fmt.Fprintf(fd, confContent)

	fd.Close()
	return nil
}

/**
1. 编译前端代码并同步编译后的代码
2. 同步配置应用上传的图片
3. 同步应用运行上传的文件
*/
func (cmd *Command) previewApp() error {
	// 编译前端代码
	err := cmd.compileCode()
	if err != nil {
		return err
	}
	// 拷贝到代码目录
	err = cmd.rsyncCode(cmd.CodeSrcPath+"/dist", cmd.CodeRunPath)
	if err != nil {
		return err
	}
	// 同步应用配置上传文件
	distAppConfigFilePath := fmt.Sprintf("%s/files/%d/%d", cmd.CodeRunPath, cmd.TenantId, cmd.AppId)
	err = cmd.rsyncAppConfigFile(cmd.AppConfigFilePath, distAppConfigFilePath)
	if err != nil {
		return err
	}
	err = cmd.rsyncAppUploadFile(cmd.AppUploadFilePath+"/upload", cmd.CodeRunPath+"/upload")
	if err != nil {
		return err
	}
	return nil
}

func (cmd *Command) compileCode() error {
	logPath := cmd.LogPath
	codeSrcPath := cmd.CodeSrcPath
	shellDir := cmd.getShellDir()
	scripts := []string{
		fmt.Sprintf("sh %s/compilecode.sh %s &>> %s", shellDir, codeSrcPath, logPath),
	}
	return ExecScripts(scripts)
}
func (cmd *Command) rsyncCode(src, dist string) error {
	scripts := []string{
		fmt.Sprintf("rsync -av --delete %s/ %s/", src, dist),
	}
	return ExecScripts(scripts)
}

func (cmd *Command) getStaticPath() string {
	staticPath := fmt.Sprintf("%s/%s", cmd.CodeSrcPath, "static")
	return staticPath
}

func (cmd *Command) publishApp() error {
	err := cmd.compileCode()
	if err != nil {
		return err
	}
	bakPath := cmd.getCodeBakPath()
	err = ExecScripts([]string{fmt.Sprintf("mkdir -p %s", bakPath)})
	if err != nil {
		return err
	}
	staticPath := cmd.getStaticPath()
	err = cmd.rsyncCode(staticPath, bakPath)
	if err != nil {
		return err
	}
	err = cmd.rsyncCode(staticPath, cmd.CodeRunPath)
	if err != nil {
		return err
	}
	return nil
}

func (cmd *Command) rollbackApp() error {
	return cmd.rsyncCode(cmd.getCodeBakPath(), cmd.CodeRunPath)
}

func (cmd *Command) getCodeBakPath() string {
	return fmt.Sprintf("%s/%s", cmd.CodeBakPath, cmd.Version)
}

func (cmd *Command) getNginxConf() string {
	nginxConfC, err := ioutil.ReadFile(config.ENV["APP_DIR"] + "/" + "nginx_conf_tpl.conf")
	if err != nil {
		panic(err)
	}
	log.Printf("%s", nginxConfC)
	conf := fmt.Sprintf(string(nginxConfC), cmd.Port, cmd.CodeRunPath, cmd.AppId, cmd.Env, cmd.LowcodeCode)
	return conf
}

func (cmd *Command) getShellDir() string {
	shellDir := config.ENV["SHELL_DIR"]
	return shellDir
}

func (cmd *Command) initCodeDir() error {
	scripts := []string{
		fmt.Sprintf("mkdir -p %s", cmd.CodeRunPath),
		fmt.Sprintf("chown -R %s:%s %s", config.ENV["RUN_USER"], config.ENV["RUN_USER"], cmd.CodeRunPath),
	}
	return ExecScripts(scripts)
}

func (cmd *Command) initDevCode() error {
	codeSrcPath := cmd.CodeSrcPath
	templateDir := config.ENV["TEMPLATE_DIR"]
	scripts := []string{
		fmt.Sprintf("mkdir -p %s", codeSrcPath),
		fmt.Sprintf("rsync -av %s/ %s/ --delete", templateDir, codeSrcPath),
	}
	return ExecScripts(scripts)
}

func (cmd *Command) reloadNginx() error {
	shellDir := cmd.getShellDir()
	logPath := cmd.LogPath
	scripts := []string{
		fmt.Sprintf("sh %s/reloadnginx.sh &>> %s", shellDir, logPath),
	}
	return ExecScripts(scripts)
}

func (cmd *Command) rsyncAppConfigFile(src, dest string) error {
	exists, _ := isFileExist(src)
	if exists {
		scripts := []string{
			fmt.Sprintf("mkdir -p %s", dest),
			fmt.Sprintf("rsync -av --delete %s %s", src, dest),
		}
		return ExecScripts(scripts)
	}
	return nil
}

func (cmd *Command) rsyncAppUploadFile(src, dest string) error {
	exists, _ := isFileExist(src)
	if exists {
		scripts := []string{
			fmt.Sprintf("ln -s  %s %s", src, dest),
		}
		return ExecScripts(scripts)
	}
	return nil
}
